#include "pixel.h"
#include <math.h>

//! \brief Transcodes RGB to XYZ
//!
//! \param px A Pixel in RGB format
//! \return A Pixel in XYZ format
//!
//!
PixelXYZ RGBtoXYZ (Pixel px)
{
    // normalize red, green, blue values
    double rlin = (double)px.r/255.0;
    double glin = (double)px.g/255.0;
    double blin = (double)px.b/255.0;

    // convert to sRGB
    double r = (rlin > 0.04045)? pow((px.r + 14.025)/(269.025), 2.4) : (rlin/12.92) ;
    double g = (glin > 0.04045)? pow((px.g + 14.025)/(269.025), 2.4) : (glin/12.92) ;
    double b = (blin > 0.04045)? pow((px.b + 14.025)/(269.025), 2.4) : (blin/12.92) ;

    PixelXYZ resu = {(r*0.4124 + g*0.3576 + b*0.1805),
                     (r*0.2126 + g*0.7152 + b*0.0722),
                     (r*0.0193 + g*0.1192 + b*0.9505)};
    return resu;
}

//! \brief Transcodes XYZ to Lab
//!
//! \param px A Pixel in XYZ format
//! \return A Pixel in Lab format
//!
//!
PixelLab XYZtoLab (PixelXYZ px)
{
    PixelLab resu = {116.0*fxyz(px.y) -16,
                     500.0*(fxyz(px.x/0.9505) - fxyz(px.y)),
                     200.0*(fxyz(px.y) - fxyz(px.z/1.0890))};
    return resu;
}

//! \brief Transcodes Lab to XYZ
//!
//! \param px A Pixel in Lab format
//! \return A Pixel in XYZ format
//!
//!
PixelXYZ LabtoXYZ (PixelLab px)
{
    double d = 0.20689655;
    double fy = (px.L+16)/116.0;
    double fx = fy + (px.a/500.0);
    double fz = fy - (px.b/200.0);

    PixelXYZ resu = {(fx > d) ? 0.9505 * (fx*fx*fx) : (fx - 0.137931)*0.12206183,
                     (fy > d) ? (fy*fy*fy) : (fy - 0.137931)*3*(d*d),
                     (fz > d) ? 1.0890 * (fz*fz*fz) : (fz - 0.137931)*0.1398478};
    return resu;
}


//! \brief Transcodes XYZ to RGB
//!
//! \param px A Pixel in XYZ format
//! \return A Pixel in RGB format
//!
//!
Pixel XYZtoRGB (PixelXYZ px)
{
    int i;
    double clin[3];
    clin[0] = px.x*3.2406 - px.y*1.5372 - px.z*0.4986; // red
    clin[1] = - px.x*0.9689 + px.y*1.8758 + px.z*0.0415; // green
    clin[2] = px.x*0.0557 - px.y*0.2040 + px.z*1.0570; // blue

    for(i=0; i<3; i++)
    {
        clin[i] = (clin[i]<0.0031308)? 12.92*clin[i] : 1.055* pow(clin[i], 0.416667) - 0.055;
    }

    Pixel resu = {(Uint8)(clin[0]*255), (Uint8)(clin[1]*255), (Uint8)(clin[2]*255)};
    return resu;
}

//! \brief Transcodes RGB to HSI
//!
//! \param px A Pixel in RGB format
//! \return A Pixel in HSI format
//!
//!
PixelHSI RGBtoHSI (Pixel px)
{
    double h, s;
    Uint8 I = (Uint8)((px.r+px.g+px.b)/3.0);

    if(px.r != 0 and px.r != 0 and px.r != 0)
    {
        if(px.r>px.g)
        {
            if(px.g>px.b) {h = ((px.g - px.b)/(px.r - px.b)) % 6; s = (double)(px.r - px.b)/px.r;}
            else {
                if(px.r>px.b) {h = ((px.g - px.b)/(px.r - px.g)) % 6; s = (double)(px.r - px.g)/px.r;}
                else {h = (px.b == px.g) ? 0 : ((px.r - px.g)/(px.b - px.g) +4); s = (double)(px.b - px.g)/px.b;} }
        }
        else
        {
            if(px.r>px.b) {h = ((px.b - px.r)/(px.g - px.b)) +2; s = (double)(px.g - px.b)/px.g;}
            else {
                if(px.b>px.g) {h = (px.b == px.r) ? 0 : (((px.r - px.g)/(px.b - px.r)) +4); s = (double)(px.b - px.r)/px.b;}
                else {h = (px.r == px.g) ? 0 : ((px.b - px.r)/(px.g - px.r) +2); s = (double)(px.g - px.r)/px.g;} }
        }
    }
    else
    {
        s = 0.0;
        if(px.r>px.g)
        {
            if(px.g>px.b) {h = ((px.g - px.b)/(px.r - px.b)) % 6;}
            else {
                if(px.r>px.b) {h = ((px.g - px.b)/(px.r - px.g)) % 6;}
                else {h = (px.b == px.g) ? 0 : ((px.r - px.g)/(px.b - px.g) +4);} }
        }
        else
        {
            if(px.r>px.b) {h = ((px.b - px.r)/(px.g - px.b)) +2;}
            else {
                if(px.b>px.g) {h = (px.b == px.r) ? 0 : (((px.r - px.g)/(px.b - px.r)) +4);}
                else {h = (px.r == px.g) ? 0 : ((px.b - px.r)/(px.g - px.r) +2);} }
        }
    }

    PixelHSI resu = {(Uint8)(h * 60.0), (Uint8)(s * 255.0), I};
    return resu;
}


//! \brief Averages two Pixel using wheights
//!
//! \param pxl1 First Pixel in Lab format
//! \param pxl2 Second Pixel in Lab format
//! \param c1 First wheight
//! \param c2 Second wheight
//! \return The averaged pixel
//!
//!
PixelLab weightedAverage(PixelLab pxl1, double c1, PixelLab pxl2, double c2)    //We are calculating
{
    PixelLab resu = {pxl1.L*c1 + pxl2.L*c2, pxl1.a*c1 + pxl2.a*c2, pxl1.b*c1 + pxl2.b*c2};
    return resu;
}

